package org.jsola.hr.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Iterators;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.jsola.common.DateKit;
import org.jsola.common.DateKit8;
import org.jsola.common.StrKit;
import org.jsola.common.TreeKit;
import org.jsola.core.Page;
import org.jsola.exception.ParamException;
import org.jsola.hr.constant.DateType;
import org.jsola.hr.constant.ExtraType;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.constant.RuleType;
import org.jsola.hr.dao.IAttendPlanDAO;
import org.jsola.hr.dao.IAttendPunchStaffDAO;
import org.jsola.hr.dto.*;
import org.jsola.hr.dto.attendrule.*;
import org.jsola.hr.dto.attendschedule.AttendPeriodDTO;
import org.jsola.hr.entity.*;
import org.jsola.hr.exception.HrException;
import org.jsola.hr.provider.IGregorianCalendarProviderService;
import org.jsola.hr.provider.IMessageProviderService;
import org.jsola.hr.provider.ISiteConfigProviderService;
import org.jsola.hr.provider.IUserProviderService;
import org.jsola.hr.query.*;
import org.jsola.hr.service.*;
import org.jsola.hr.vo.*;
import org.jsola.log.common.LogKit;
import org.jsola.log.constant.ActionType;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.util.HtmlUtils;
import tk.mybatis.mapper.entity.Example;

import java.text.ParseException;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 考勤规则
 *
 * @author wxz
 */
@Slf4j
@Service("hrAttendRuleServiceImpl")
public class AttendRuleServiceImpl implements IAttendRuleService {

    @Autowired
    private IAttendPunchDeptService attendPunchDeptService;
    @Autowired
    private IAttendPunchStaffService attendPunchStaffService;
    @Autowired
    private IAttendWhitelistService attendWhitelistService;
    @Autowired
    private IAttendReportService attendReportService;
    @Autowired
    private IAttendPunchTimeService attendPunchTimeService;
    @Autowired
    private IAttendCalendarService attendCalendarService;
    @Autowired
    private IAttendPlanService attendPlanService;
    @Autowired
    private IAttendExtraJsonService attendExtraJsonService;
    @Autowired
    private IAttendPlanDAO attendPlanDAO;
    @Autowired
    private IEmpDepService empDepService;
    @Autowired
    private IAttendPunchStaffDAO attendPunchStaffDAO;
    @Autowired
    private IDepInfoService depInfoService;
    @Autowired
    private IEmpInfoService empInfoService;
    @Autowired
    private IAttendOvertimeRuleService attendOvertimeRuleService;
    @Autowired
    private IAttendScheduleService attendScheduleService;
    @Autowired
    private ICompanyService companyService;
    @Autowired
    private IAttendShiftService attendShiftService;
    @Autowired
    private IAttendShiftScheduleCycleService attendShiftScheduleCycleService;
    @Autowired
    private IAttendShiftStaffScheduleService attendShiftStaffScheduleService;

    @Autowired
    private ITaskService taskService;

    @Autowired
    private ISiteConfigProviderService siteConfigProviderService;

    @Autowired
    private IUserProviderService userProviderService;

    @Autowired
    private IMessageProviderService messageProviderService;

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTask;

    @Autowired
    private IAttendSysNoticeTaskService attendSysNoticeTaskService;

    @Autowired
    private IGregorianCalendarProviderService gregorianCalendarProviderService;
    
    /**
     * 保存考勤规则
     *
     * @param attendRuleAddDTO 考勤规则
     * @return
     */
    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public AttendPlanVO save(AttendRuleAddDTO attendRuleAddDTO, TokenUser tokenUser) {

        // 查询公司信息
        CompanyVO companyInfo = companyService.selectBaseCompanyById(attendRuleAddDTO.getCompanyId(), tokenUser.getSiteId());
        if(Objects.isNull(companyInfo)){
            throw new ParamException("未找到公司相关信息");
        }

        String name = attendRuleAddDTO.getPlanName();
        if (StrKit.isNotEmpty(name)) {
            name = name.trim();
            if (name.length() > 10) {
                throw new ParamException("名称长度不能超过10");
            }
        }
        attendRuleAddDTO.setPlanName(name);
    	// 同一个公司下的规则名称不能重复
        AttendPlanQuery attendPlanQuery = new AttendPlanQuery();
        attendPlanQuery.setCompanyId(companyInfo.getId());
        attendPlanQuery.setName(attendRuleAddDTO.getPlanName());
        List<AttendPlanDO> attendPlanDOList = attendPlanService.selectDO(attendPlanQuery, tokenUser.getSiteId());
        if (attendPlanDOList.size() > 0) {
            throw new HrException("规则名称不能重复");
        }

        Date now = DateKit.getToday();

        // 如果规则类型是按班次上下班，则临时考勤排班ID不能为空
        if (attendRuleAddDTO.getPlanType().equals(RuleType.BY_SHIFT.getValue())) {
            if (StringUtils.isBlank(attendRuleAddDTO.getTmpAttendShiftId())) {
                throw new HrException("请编辑排班");
            }
        }

        // 如果休息时段不为空，则将休息时段添加到打卡时段中
        List<AttendRulePunchTimeAddDTO> punchTimeLst = attendRuleAddDTO.getPunchTimeList();
        if (!CollectionUtils.isEmpty(punchTimeLst)) {
            for (int i = 0; i < punchTimeLst.size(); i++) {
                AttendRulePunchTimeAddDTO attendRulePunchTimeAddDTO = punchTimeLst.get(i);
                if (Objects.nonNull(attendRulePunchTimeAddDTO.getRestPeriod())) {

                }
            }
        }

        // 初始化打卡时间高级设置
        this.initAdvanced(attendRuleAddDTO);

        // 考勤方案
        String attendPlanId = UUID.randomUUID().toString().replace("-", "");

        // 如果打卡部门不为空
        List<AttendRuleDeptAddDTO> deptList = attendRuleAddDTO.getDeptList();
        if (!CollectionUtils.isEmpty(deptList)) {
            this.saveAttendRuleDept(attendPlanId, deptList, companyInfo, tokenUser);
        }
        // 如果打卡人员不为空
        List<AttendRuleStaffAddDTO> staffList = attendRuleAddDTO.getStaffList();
        if (!CollectionUtils.isEmpty(staffList)) {
            // 考勤打卡规则员工重复添加的集合
            List<AttendRuleStaffAddDTO> staffListRepeat = new ArrayList<>();
            // 校验公司下其他考勤打卡规则配置员工是否有重复
            AttendPlanQuery attendPlanQueryCheck = new AttendPlanQuery();
            attendPlanQueryCheck.setCompanyId(companyInfo.getId());
            List<AttendPlanDO> attendPlanDOListCheck = attendPlanService.selectDO(attendPlanQueryCheck, tokenUser.getSiteId());
            if(!CollectionUtils.isEmpty(attendPlanDOListCheck)){
                attendPlanDOListCheck.forEach(attendPlanDO -> {
                    // 查询额外数据
                    AttendExtraJsonQuery attendExtraJsonQuery = new AttendExtraJsonQuery();
                    attendExtraJsonQuery.setAttendPalnId(attendPlanDO.getId());
                    attendExtraJsonQuery.setExtraType(ExtraType.ATTEND_PUNCH_STAFF.getValue());
                    List<AttendExtraJsonDO> attendExtraJsonDOList = attendExtraJsonService.selectDO(attendExtraJsonQuery, tokenUser.getSiteId());
                    if (!CollectionUtils.isEmpty(attendExtraJsonDOList)) {
                        // 查询已存在考勤规则中的员工列表
                        List<AttendRuleStaffAddDTO> attendRuleStaffAddDTOList = JSON.parseArray(attendExtraJsonDOList.get(0).getJsonData(), AttendRuleStaffAddDTO.class);
                        // 交集
                        List<AttendRuleStaffAddDTO> attendRuleStaffAddDTOListRepeat = staffList.stream()
                                .filter(item ->
                                        attendRuleStaffAddDTOList.stream()
                                                .map(AttendRuleStaffAddDTO::getEmpInfoId)
                                                .collect(Collectors.toList())
                                                .contains(item.getEmpInfoId()))
                                .collect(Collectors.toList());
                        staffListRepeat.addAll(attendRuleStaffAddDTOListRepeat);
                    }
                });
            }
            if(!CollectionUtils.isEmpty(staffListRepeat)){
                String staffListRepeatStr = staffListRepeat.stream().map(AttendRuleStaffAddDTO::getEmpInfoName).collect(Collectors.joining("，"));
                throw new ParamException("打卡人员 已存在其他考勤规则中：" + staffListRepeatStr);
            }
            this.saveStaff(attendPlanId, staffList, companyInfo, tokenUser);
        }

        if (CollectionUtils.isEmpty(deptList) && CollectionUtils.isEmpty(staffList)) {
        	throw new HrException("打卡部门或人员不能为空");
        }

        // 如果有白名单
        List<AttendRuleWhitelistAddDTO> whitelist = attendRuleAddDTO.getWhitelist();
        if (!CollectionUtils.isEmpty(whitelist)) {
            this.saveWhitelist(attendPlanId, whitelist, companyInfo, tokenUser);
        }

        // 保存汇报对象
//        List<AttendRuleReportAddDTO> reportList = attendRuleAddDTO.getReportList();
//        if (CollectionUtils.isEmpty(reportList)) {
//            throw new HrException("汇报对象不能为空");
//        }
//        this.saveReport(attendPlanId, reportList, companyInfo, tokenUser);

        // 打卡位置和打卡WIFI 必须要有一个
        if (CollectionUtils.isEmpty(attendRuleAddDTO.getLocationList()) && CollectionUtils.isEmpty(attendRuleAddDTO.getWifiList())) {
            throw new HrException("打卡位置和打卡WIFI 必须要有一个");
        }

        // 如果规则类型是固定上下班则打卡时间不能为空
        List<AttendRulePunchTimeAddDTO> punchTimeList = attendRuleAddDTO.getPunchTimeList();
        switch (RuleType.find(attendRuleAddDTO.getPlanType())){
            case FIXED_TIME:
                // 固定时间上下班
                if (punchTimeList == null) {
                    throw new HrException("打卡时间不能为空");
                }
                // 保存打卡时间
                this.savepPunchTime(attendPlanId, punchTimeList, tokenUser);

                break;
            case BY_SHIFT:
                // 按班次上下班
                // 如果规则类型是按班次上下班，则临时考勤排班ID不能为空
                String tmpAttendShiftId = attendRuleAddDTO.getTmpAttendShiftId();
                // 更新考勤规则ID
                attendScheduleService.saveRuleForSchedule(tmpAttendShiftId, attendPlanId, tokenUser);
                break;
            case FREE_WORK:
                // 自由上下班

                break;
        }

        // 保存特殊日期-必须打卡的日期
        List<NeedPunchDateAddDTO> needPunchDateList = attendRuleAddDTO.getNeedPunchDateList();
        if (!CollectionUtils.isEmpty(needPunchDateList)) {
            this.saveNeedPunchDateList(attendPlanId, needPunchDateList, tokenUser);
        }

        // 保存特殊日期-不必打卡的日期
        List<NoNeedPunchDateAddDTO> noNeedPunchDateList = attendRuleAddDTO.getNoNeedPunchDateList();
        if (!CollectionUtils.isEmpty(noNeedPunchDateList)) {
            this.saveNoNeedPunchDateList(attendPlanId, noNeedPunchDateList, tokenUser);
        }

        // 保存加班规则
        // 如果加班规则是以打卡时间为准，则初始化加班规则 加班规则", example = "1-以加班申请为准,2-以打卡时间为准,3-以加班申请核算打卡记录
//        if (attendRuleAddDTO.getOvertimeRule() == 2) {
//            // 初始化 工作日加班
//            WorkDayRuleDTO workDayRuleDTO = AttendKit.initWorkDayRule(attendRuleAddDTO.getWorkDayRuleDTO());
//            attendRuleAddDTO.setWorkDayRuleDTO(workDayRuleDTO);
//
//            // 初始化非工作日加班
//            NonWorkDayRuleDTO nonWorkDayRuleDTO = AttendKit.initNonWorkDayRule(attendRuleAddDTO.getNonWorkDayRuleDTO());
//            attendRuleAddDTO.setNonWorkDayRuleDTO(nonWorkDayRuleDTO);
//        }
//
//        this.saveOvertimeRule(attendPlanId, attendRuleAddDTO.getOvertimeRule()
//                , attendRuleAddDTO.getWorkDayRuleDTO(), attendRuleAddDTO.getNonWorkDayRuleDTO(), tokenUser);

        // 保存考勤方案
        AttendPlanDO attendPlanDO = new AttendPlanDO();

        attendPlanDO.setId(attendPlanId);
        attendPlanDO.setCompanyId(companyInfo.getId());
        attendPlanDO.setCompanyName(companyInfo.getName());
        attendPlanDO.setName(attendRuleAddDTO.getPlanName());
        attendPlanDO.setPlanType(attendRuleAddDTO.getPlanType());
        if (!CollectionUtils.isEmpty(attendRuleAddDTO.getLocationList())) {
        	attendPlanDO.setPunchLocationJson(HtmlUtils.htmlUnescape(JSON.toJSONString(attendRuleAddDTO.getLocationList())));
        }
        if (!CollectionUtils.isEmpty(attendRuleAddDTO.getWifiList())) {
        	attendPlanDO.setPunchWifiJson(JSON.toJSONString(attendRuleAddDTO.getWifiList()));
        }
        attendPlanDO.setOvertimeRule(attendRuleAddDTO.getOvertimeRule());
        attendPlanDO.setIsSynchLegalHoliday(attendRuleAddDTO.getIsSynchLegalHoliday());
        attendPlanDO.setIsRemind(attendRuleAddDTO.getIsRemind());
        attendPlanDO.setRemindBefore(attendRuleAddDTO.getRemindBefore());
        attendPlanDO.setRemindAfter(attendRuleAddDTO.getRemindAfter());
        attendPlanDO.setIsTakePhoto(attendRuleAddDTO.getIsTakePhoto());
        attendPlanDO.setIsOnlyPhoto(attendRuleAddDTO.getIsOnlyPhoto());
        attendPlanDO.setPunchRange(attendRuleAddDTO.getPunchRange());
        attendPlanDO.setIsMakeUp(attendRuleAddDTO.getIsMakeUp());
        attendPlanDO.setMaxMakeUpCounts(attendRuleAddDTO.getMaxMakeUpCounts());
        attendPlanDO.setTakeEffect(attendRuleAddDTO.getTakeEffect());
        if (attendRuleAddDTO.getTakeEffect() != null && attendRuleAddDTO.getTakeEffect() == 1) {
            Date startEffectTime = null;
            try {
                startEffectTime = DateUtils.parseDate(org.jsola.hr.common.DateUtils.getBeforeDateByDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd")), "yyyy-MM-dd");
            } catch (ParseException e) {
                e.printStackTrace();
            }
            attendPlanDO.setStartEffectTime(startEffectTime);
        }
        attendPlanDO.setGmtCreate(now);
        attendPlanDO.setGmtModified(now);
        attendPlanDO.setCreateUserId(tokenUser.getUserId());
        attendPlanDO.setModifiedUserId(tokenUser.getUserId());
        attendPlanDO.setSiteId(tokenUser.getSiteId());
        attendPlanDO.setValid(true);

        attendPlanDAO.insert(attendPlanDO);

        // 如果是立即生效  1-明日生效，2-立即生效
        if (attendPlanDO.getTakeEffect() == 2) {
            try {
                sendNotice(attendPlanDO.getId());
            } catch (Exception e) {
                log.error("保存考勤打卡消息通知定时任务异常", e);
            }
        }

        LogKit.log("保存考勤规则，主键ID:"+  attendPlanDO.getId() + "考勤规则名称:" + attendPlanDO.getName()
                                , ActionType.SAVE.getValue(), "attendPlan", tokenUser);

        return attendPlanDO.to(AttendPlanVO.class);
    }

    private void validAttendRuleSaveParams(AttendRuleAddDTO attendRuleAddDTO) {

        // 打卡人员和打卡部门不能同时为空
        if (attendRuleAddDTO.getStaffList() == null && attendRuleAddDTO.getDeptList() == null) {
            throw new HrException("打卡人员不能为空");
        }
    }

    /**
     * 校验打卡时段
     *
     * @param attendRuleAddDTO
     */
    private void validPunchTime(AttendRuleAddDTO attendRuleAddDTO) {

        // 如果考勤规则是按班次上下班
        if (attendRuleAddDTO.getPlanType().equals(RuleType.BY_SHIFT.getValue())) {
            return;
        }

        List<AttendRulePunchTimeAddDTO> punchTimeList = attendRuleAddDTO.getPunchTimeList();
        if (punchTimeList == null) {
            throw new HrException("打卡时间不能为空");
        }

        // 不能有重复的工作周
        for (AttendRulePunchTimeAddDTO time1 : punchTimeList) {

            List<String> weekList1 = time1.getWorkWeekList();

            for (AttendRulePunchTimeAddDTO time2 : punchTimeList) {
                List<String> weekList2 = time2.getWorkWeekList();
                boolean flag = Iterators.elementsEqual(weekList1.iterator(), weekList2.iterator());
                if (flag) {
                    throw new HrException("不能有重复的工作周");
                }
            }
        }

        // 时间段
        for (AttendRulePunchTimeAddDTO punchTime : punchTimeList) {

            List<AttendPeriodDTO> periodList = punchTime.getPeriodList();
            if (periodList == null) {
                continue;
            }

            for (int i = 0; i < periodList.size(); i++) {

                String startPeriod = periodList.get(i).getStartPeriod();
                String endPeriod = periodList.get(i).getEndPeriod();
                // 应晚于上班时间
                int startInt = Integer.valueOf(startPeriod.replace(":", "")).intValue();
                int endInt = Integer.valueOf(endPeriod.replace(":", "")).intValue();
                if (endInt == startInt) {
                    throw new HrException("应晚于上班时间");
                }

                //
                if (i  > 0) {

                    int pre = i - 1;
                    String preEndPeriod = periodList.get(pre).getEndPeriod();
                    int preEndInt = Integer.valueOf(preEndPeriod.replace(":", "")).intValue();

                    if (preEndInt >= startInt || preEndInt >= endInt) {
                        throw new HrException("应晚于上段下班时间");
                    }
                }

            }
        }
    }

    /**
     * 初始化打卡时间高级设置
     *
     * @param attendRuleAddDTO
     */
    private void initAdvanced(AttendRuleAddDTO attendRuleAddDTO) {

        // 如果考勤规则是按班次上下班或自由上下班
        if (attendRuleAddDTO.getPlanType().equals(RuleType.BY_SHIFT.getValue())
                                        || attendRuleAddDTO.getPlanType().equals(RuleType.FREE_WORK.getValue())) {
            return;
        }

        List<AttendRulePunchTimeAddDTO> punchTimeList = attendRuleAddDTO.getPunchTimeList();

        for (int i = 0; i < punchTimeList.size(); i++) {

            AttendRulePunchTimeAddDTO attendRulePunchTime = punchTimeList.get(i);
            // 允许迟到早退；1-允许,0-不允许
            if (attendRulePunchTime.getIsAllowedLateEarly() == null) {
                attendRulePunchTime.setIsAllowedLateEarly(0);
            }
            // 上班允许迟到(分钟)
            if (attendRulePunchTime.getLateMinute() == null) {
                attendRulePunchTime.setLateMinute(0);
            }
            // 下班允许早退(分钟)
            if (attendRulePunchTime.getEarlyMinute() == null) {
                attendRulePunchTime.setEarlyMinute(0);
            }
            // 允许晚走晚到;1-允许,0-不允许
            if (attendRulePunchTime.getIsAllowLateLate() == null) {
                attendRulePunchTime.setIsAllowLateLate(0);
            }
            // 下班晚走(分钟)
            if (attendRulePunchTime.getOffWorkLateMinute() == null) {
                attendRulePunchTime.setOffWorkLateMinute(0);
            }
            // 上班晚到(分钟)
            if (attendRulePunchTime.getOnWorkLateMinute() == null) {
                attendRulePunchTime.setOnWorkLateMinute(0);
            }
            // 最晚下班打卡时间
            if (StringUtils.isBlank(attendRulePunchTime.getLatestPunchTime())) {
                attendRulePunchTime.setLatestPunchTime("0");
            }
            // 最早上班打卡时间
            if (StringUtils.isBlank(attendRulePunchTime.getEarliestPunchTime())) {
                attendRulePunchTime.setEarliestPunchTime("0");
            }
            // 下班是否需要打卡;1-是,0-否
            if (attendRulePunchTime.getAfterWorkNeedPunch() == null) {
                attendRulePunchTime.setAfterWorkNeedPunch(0);
            }
        }

    }

    @Override
    public AttendRuleVO findById(String id, String siteId) {

        // 根据ID查询考勤方案
        AttendPlanVO attendPlanVO = attendPlanService.selectById(id, siteId);
        if (attendPlanVO == null) {
            throw new HrException("查询为空");
        }

        // 打卡部门
        List<AttendRuleDeptAddDTO> deptList = new ArrayList<>();
        // 打卡人员
        List<AttendRuleStaffAddDTO> staffList = new ArrayList<>();
        // 白名单
        List<AttendRuleWhitelistAddDTO> whitelist = new ArrayList<>();
        // 汇报对象
        List<AttendRuleReportAddDTO> reportList = new ArrayList<>();
        // 打卡地点-位置
        List<AttendRuleLocationAddDTO> locationList = new ArrayList<>();
        if (StringUtils.isNotEmpty(attendPlanVO.getPunchLocationJson())) {
            locationList = JSON.parseArray(attendPlanVO.getPunchLocationJson(), AttendRuleLocationAddDTO.class);
        }
        // 打卡地点-wifi
        List<AttendRuleWifiAddDTO> wifiList = new ArrayList<>();
        if (StringUtils.isNotEmpty(attendPlanVO.getPunchWifiJson())) {
            wifiList = JSON.parseArray(attendPlanVO.getPunchWifiJson(), AttendRuleWifiAddDTO.class);
        }
        
        // 打卡时间
        List<AttendRulePunchTimeAddDTO> punchTimeList = new ArrayList<>();
        // 特殊日期-必须打卡的日期
        List<NeedPunchDateAddDTO> needPunchDateList = new ArrayList<>();
        // 特殊日期-不必打卡的日期
        List<NoNeedPunchDateAddDTO> noNeedPunchDateList = new ArrayList<>();
        // 工作日加班规则
        WorkDayRuleDTO workDayRuleDTO = null;
        // 非工作日加班规则
        NonWorkDayRuleDTO nonWorkDayRuleDTO = null;

        // 查询考勤方案额外数据
        AttendExtraJsonQuery attendExtraJsonQuery = new AttendExtraJsonQuery();
        attendExtraJsonQuery.setAttendPalnId(id);
        List<AttendExtraJsonDO> attendExtraJsonList = attendExtraJsonService.selectDO(attendExtraJsonQuery, siteId);
        if (attendExtraJsonList.size() > 0) {
        	
        	for (AttendExtraJsonDO attendExtraJson : attendExtraJsonList) {
				
        		// 打卡部门
        		if (attendExtraJson.getExtraType().equals(ExtraType.ATTEND_PUNCH_DEPT.getValue())) {
        			deptList = JSON.parseArray(attendExtraJson.getJsonData(), AttendRuleDeptAddDTO.class);
        		}
        		// 打卡人员
        		if (attendExtraJson.getExtraType().equals(ExtraType.ATTEND_PUNCH_STAFF.getValue())) {
        			staffList = JSON.parseArray(attendExtraJson.getJsonData(), AttendRuleStaffAddDTO.class);
        		}
        		// 白名单
        		if (attendExtraJson.getExtraType().equals(ExtraType.ATTEND_WHITELIST.getValue())) {
        			whitelist = JSON.parseArray(attendExtraJson.getJsonData(), AttendRuleWhitelistAddDTO.class);
        		}
        		// 汇报对象
        		if (attendExtraJson.getExtraType().equals(ExtraType.ATTEND_REPORT.getValue())) {
        			reportList = JSON.parseArray(attendExtraJson.getJsonData(), AttendRuleReportAddDTO.class);
        		}
        		// 打卡时间
        		if (attendExtraJson.getExtraType().equals(ExtraType.ATTEND_PUNCH_TIME.getValue())) {
        			punchTimeList = JSON.parseArray(attendExtraJson.getJsonData(), AttendRulePunchTimeAddDTO.class);

        			// 如果有休息时段，则单独赋值
//                    if (punchTimeList != null && punchTimeList.size() > 0) {
//                        for (int i = 0; i < punchTimeList.size(); i++) {
//
//                            AttendRulePunchTimeAddDTO attendRulePunchTimeAddDTO = punchTimeList.get(i);
//
//                            List<AttendPeriodDTO> periodList = attendRulePunchTimeAddDTO.getPeriodList();
//                            List<AttendPeriodDTO> newPeriodList = new ArrayList<>();
//                            if (!CollectionUtils.isEmpty(periodList)) {
//                                for (int j = 0; j < periodList.size(); j++) {
//                                    AttendPeriodDTO attendPeriodDTO = periodList.get(j);
//                                    if (attendPeriodDTO.getType() == 1) {
//                                        attendRulePunchTimeAddDTO.setRestPeriod(attendPeriodDTO);
//                                    }
//                                    else {
//                                        newPeriodList.add(attendPeriodDTO);
//                                    }
//                                }
//                            }
//                            if (newPeriodList.size() > 0) {
//                                periodList = newPeriodList;
//                            }
//                        }
//                    }
                    // end if

        		}
        		// 考勤日历(需要打卡)
        		if (attendExtraJson.getExtraType().equals(ExtraType.ATTEND_CALENDAR_NEED_PUNCH.getValue())) {
        			// 特殊日期-必须打卡的日期
        			needPunchDateList = JSON.parseArray(attendExtraJson.getJsonData(), NeedPunchDateAddDTO.class);
        		}
        		// 考勤日历(不需要打卡)
        		if (attendExtraJson.getExtraType().equals(ExtraType.ATTEND_PUNCH_DEPT_NO_NEED_PUNCH.getValue())) {
        			// 特殊日期-不必打卡的日期
        			noNeedPunchDateList = JSON.parseArray(attendExtraJson.getJsonData(), NoNeedPunchDateAddDTO.class);
        		}
        		// 加班规则
                if (attendExtraJson.getExtraType().equals(ExtraType.OVERTIME_RULE.getValue())) {
                    AttendOvertimeRuleVO attendOvertimeRuleVO = JSON.parseObject(attendExtraJson.getJsonData(), AttendOvertimeRuleVO.class);
                    if (StringUtils.isNotBlank(attendOvertimeRuleVO.getWorkDayRuleJson())) {
                        workDayRuleDTO = JSON.parseObject(attendOvertimeRuleVO.getWorkDayRuleJson(), WorkDayRuleDTO.class);
                    }
                    if (StringUtils.isNotBlank(attendOvertimeRuleVO.getNonWorkDayRuleJson())) {
                        nonWorkDayRuleDTO = JSON.parseObject(attendOvertimeRuleVO.getNonWorkDayRuleJson(), NonWorkDayRuleDTO.class);
                    }
                }
			}

        }

        AttendRuleVO attendRuleVO = new AttendRuleVO();

        attendRuleVO.setId(attendPlanVO.getId());
        // 方案名称
        attendRuleVO.setPlanName(attendPlanVO.getName());
        // 方案类型 ; 1-固定时间上下班,2-按班次上下班,3-自由上下班
        attendRuleVO.setPlanType(attendPlanVO.getPlanType());
        // 打卡部门
        attendRuleVO.setDeptList(deptList);
        // 打卡人员
        attendRuleVO.setStaffList(staffList);
        // 白名单
        attendRuleVO.setWhitelist(whitelist);
        // 汇报对象
        attendRuleVO.setReportList(reportList);
        // 打卡地点-位置
        attendRuleVO.setLocationList(locationList);
        // 打卡地点-wifi
        attendRuleVO.setWifiList(wifiList);
        // 打卡时间
        attendRuleVO.setPunchTimeList(punchTimeList);
        // 特殊日期-必须打卡的日期
        attendRuleVO.setNeedPunchDateList(needPunchDateList);
        // 特殊日期-不必打卡的日期
        attendRuleVO.setNoNeedPunchDateList(noNeedPunchDateList);
        // 加班规则
        attendRuleVO.setOvertimeRule(attendPlanVO.getOvertimeRule());
        // 工作日加班规则
        attendRuleVO.setWorkDayRuleDTO(workDayRuleDTO);
        // 非工作日加班规则
        attendRuleVO.setNonWorkDayRuleDTO(nonWorkDayRuleDTO);
        // 否同步中国节假日;1-是,0-否
        attendRuleVO.setIsSynchLegalHoliday(attendPlanVO.getIsSynchLegalHoliday());
        // 是否手机提醒;1-是,0-否
        attendRuleVO.setIsRemind(attendPlanVO.getIsRemind());
        // 上班前x分钟
        attendRuleVO.setRemindBefore(attendPlanVO.getRemindBefore());
        // 下班后x分钟
        attendRuleVO.setRemindAfter(attendPlanVO.getRemindAfter());
        // 员工打卡时必须拍照;1-是,0-否
        attendRuleVO.setIsTakePhoto(attendPlanVO.getIsTakePhoto());
        // 备注不允许上传本地图片
        attendRuleVO.setIsOnlyPhoto(attendPlanVO.getIsOnlyPhoto());
        // 范围外打卡
        attendRuleVO.setPunchRange(attendPlanVO.getPunchRange());
        // 是否补卡申请
        attendRuleVO.setIsMakeUp(attendPlanVO.getIsMakeUp());
        // 每月不高于x下次
        attendRuleVO.setMaxMakeUpCounts(attendPlanVO.getMaxMakeUpCounts());
        // 生效时间;1-明日生效，2-立即生效
        attendRuleVO.setTakeEffect(attendPlanVO.getTakeEffect());

        return attendRuleVO;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(AttendRuleUpdateDTO attendRuleUpdateDTO, TokenUser tokenUser) {

        // 根据考勤方案ID查询
        AttendPlanVO attendPlanVO = attendPlanService.selectById(attendRuleUpdateDTO.getId(), tokenUser.getSiteId());
        if (attendPlanVO == null) {
            throw new HrException("考勤规则不存在");
        }

        // 查询公司信息
        CompanyVO companyInfo = companyService.selectBaseCompanyById(attendRuleUpdateDTO.getCompanyId(), tokenUser.getSiteId());
        String name = attendRuleUpdateDTO.getPlanName();
        if (StrKit.isNotEmpty(name)) {
            name = name.trim();
            if (name.length() > 10) {
                throw new ParamException("名称长度不能超过10");
            }
        }
        attendRuleUpdateDTO.setPlanName(name);
        // 考勤规则名称不能重复(排除当前的考勤规则)
        if (!StringUtils.equals(attendPlanVO.getName(), attendRuleUpdateDTO.getPlanName())) {

            AttendPlanQuery attendPlanQuery = new AttendPlanQuery();
            attendPlanQuery.setName(attendRuleUpdateDTO.getPlanName());
            List<AttendPlanDO> attendPlanDOList = attendPlanService.selectDO(attendPlanQuery, tokenUser.getSiteId());
            if (attendPlanDOList.size() > 0) {
                for (AttendPlanDO attendPlanDO : attendPlanDOList) {
                    if (attendPlanDO.getName().equals(attendRuleUpdateDTO.getPlanName())
                                            && !attendPlanDO.getId().equals(attendPlanVO.getId())) {
                        throw new HrException("考勤规则名称不能重复");
                    }
                }
            }
        }

        // 考勤方案
        String attendPlanId = "";
        String tmpAttendPlanId = "";
        // 如果规则生效时间是明日生效 1-明日生效，2-立即生效
        if (attendRuleUpdateDTO.getTakeEffect() == 1) {
            attendPlanId = UUID.randomUUID().toString().replace("-", "");
            // 将旧版本的规则设置为不显示
            AttendPlanDO attendPlan = new AttendPlanDO();
            attendPlan.setId(attendPlanVO.getId());
            attendPlan.setValid(false);
            attendPlanService.updateByIdSelective(attendPlan, tokenUser.getSiteId(), tokenUser.getUserId());

            tmpAttendPlanId = attendPlanVO.getId();
        }
        else {
            attendPlanId = attendPlanVO.getId();
            // 删除打卡部门
            attendPunchDeptService.deleteByAttendPlanId(attendPlanVO.getId(), tokenUser.getSiteId());
            // 删除打卡人员
            attendPunchStaffService.deleteByAttendPlanId(attendPlanVO.getId(), tokenUser.getSiteId());
            // 删除白名单
            attendWhitelistService.deleteByAttendPlanId(attendPlanVO.getId(), tokenUser.getSiteId());
            // 删除汇报对象
            attendReportService.deleteByAttendPlanId(attendPlanVO.getId(), tokenUser.getSiteId());
            // 删除打卡时间
            attendPunchTimeService.deleteByAttendPlanId(attendPlanVO.getId(), tokenUser.getSiteId());
            // 删除特殊日期-必须打卡的日期;删除特殊日期-不必打卡的日期
            attendCalendarService.deleteByAttendPlanId(attendPlanVO.getId(), tokenUser.getSiteId());
            // 删除加班规则
            attendOvertimeRuleService.deleteByAttendPlanId(attendPlanVO.getId(), tokenUser.getSiteId());
            // 删除考勤额外数据
            attendExtraJsonService.deleteByAttendPlanId(attendPlanVO.getId(), tokenUser);
        }

        // 如果休息时段不为空，则将休息时段添加到打卡时段中
//        List<AttendRulePunchTimeAddDTO> punchTimeLst = attendRuleUpdateDTO.getPunchTimeList();
//        if (punchTimeLst != null && punchTimeLst.size() > 0) {
//            for (int i = 0; i < punchTimeLst.size(); i++) {
//                AttendRulePunchTimeAddDTO attendRulePunchTimeAddDTO = punchTimeLst.get(i);
//                if (attendRulePunchTimeAddDTO.getRestPeriod() != null) {
//                    attendRulePunchTimeAddDTO.getPeriodList().add(attendRulePunchTimeAddDTO.getRestPeriod());
//                }
//            }
//        }

        // 保存打卡部门
        if (attendRuleUpdateDTO.getDeptList() != null && attendRuleUpdateDTO.getDeptList().size() > 0) {
        	this.saveAttendRuleDept(attendPlanId, attendRuleUpdateDTO.getDeptList(), companyInfo, tokenUser);
        }
        // 保存打卡人员
        if (attendRuleUpdateDTO.getStaffList() != null && attendRuleUpdateDTO.getStaffList().size() > 0) {
            List<AttendRuleStaffAddDTO> staffList = attendRuleUpdateDTO.getStaffList();
            // 考勤打卡规则员工重复添加的集合
            List<AttendRuleStaffAddDTO> staffListRepeat = new ArrayList<>();
            // 校验公司下其他考勤打卡规则配置员工是否有重复
            Example example = new Example(AttendPlanDO.class);
            example.and()
                    .andEqualTo("companyId", companyInfo.getId())
                    .andNotEqualTo("id", attendPlanVO.getId())
                    .andEqualTo("valid", true)
                    .andEqualTo("siteId", tokenUser.getSiteId());
            List<AttendPlanDO> attendPlanDOListCheck = attendPlanDAO.selectByExample(example);
            if(!CollectionUtils.isEmpty(attendPlanDOListCheck)){
                attendPlanDOListCheck.forEach(attendPlanDO -> {
                    // 查询额外数据
                    AttendExtraJsonQuery attendExtraJsonQuery = new AttendExtraJsonQuery();
                    attendExtraJsonQuery.setAttendPalnId(attendPlanDO.getId());
                    attendExtraJsonQuery.setExtraType(ExtraType.ATTEND_PUNCH_STAFF.getValue());
                    List<AttendExtraJsonDO> attendExtraJsonDOList = attendExtraJsonService.selectDO(attendExtraJsonQuery, tokenUser.getSiteId());
                    if (!CollectionUtils.isEmpty(attendExtraJsonDOList)) {
                        // 查询已存在考勤规则中的员工列表
                        List<AttendRuleStaffAddDTO> attendRuleStaffAddDTOList = JSON.parseArray(attendExtraJsonDOList.get(0).getJsonData(), AttendRuleStaffAddDTO.class);
                        // 交集
                        List<AttendRuleStaffAddDTO> attendRuleStaffAddDTOListRepeat = staffList.stream()
                                .filter(item ->
                                        attendRuleStaffAddDTOList.stream()
                                                .map(AttendRuleStaffAddDTO::getEmpInfoId)
                                                .collect(Collectors.toList())
                                                .contains(item.getEmpInfoId()))
                                .collect(Collectors.toList());
                        staffListRepeat.addAll(attendRuleStaffAddDTOListRepeat);
                    }
                });
            }
            if(!CollectionUtils.isEmpty(staffListRepeat)){
                String staffListRepeatStr = staffListRepeat.stream().map(AttendRuleStaffAddDTO::getEmpInfoName).collect(Collectors.joining("，"));
                throw new ParamException("打卡人员 已存在其他考勤规则中：" + staffListRepeatStr);
            }
        	this.saveStaff(attendPlanId, attendRuleUpdateDTO.getStaffList(), companyInfo, tokenUser);
        }
        // 保存白名单
        if (attendRuleUpdateDTO.getWhitelist() != null && attendRuleUpdateDTO.getWhitelist().size() > 0) {
        	this.saveWhitelist(attendPlanId, attendRuleUpdateDTO.getWhitelist(), companyInfo, tokenUser);
        }
        // 汇报对象
        if (attendRuleUpdateDTO.getReportList() != null && attendRuleUpdateDTO.getReportList().size() > 0) {
        	this.saveReport(attendPlanId, attendRuleUpdateDTO.getReportList(), companyInfo, tokenUser);
        }
        // 打卡时间
        if (attendRuleUpdateDTO.getPunchTimeList() != null && attendRuleUpdateDTO.getPunchTimeList().size() > 0) {
        	this.savepPunchTime(attendPlanId, attendRuleUpdateDTO.getPunchTimeList(), tokenUser);
        }
        // 保存特殊日期-必须打卡的日期
        if (attendRuleUpdateDTO.getNeedPunchDateList() != null && attendRuleUpdateDTO.getNeedPunchDateList().size() > 0) {
        	this.saveNeedPunchDateList(attendPlanId, attendRuleUpdateDTO.getNeedPunchDateList(), tokenUser);
        }
        // 保存特殊日期-不必打卡的日期
        if (attendRuleUpdateDTO.getNoNeedPunchDateList() != null && attendRuleUpdateDTO.getNoNeedPunchDateList().size() > 0) {
        	this.saveNoNeedPunchDateList(attendPlanId, attendRuleUpdateDTO.getNoNeedPunchDateList(), tokenUser);
        }
        // 保存加班规则
        this.saveOvertimeRule(attendPlanId, attendRuleUpdateDTO.getOvertimeRule()
                , attendRuleUpdateDTO.getWorkDayRuleDTO(), attendRuleUpdateDTO.getNonWorkDayRuleDTO(), tokenUser);

        // 更新考勤方案
        AttendPlanUpdateDTO attendPlanUpdateDTO = new AttendPlanUpdateDTO();

        attendPlanUpdateDTO.setId(attendPlanId);
        // 公司ID
        attendPlanUpdateDTO.setCompanyId(attendPlanVO.getCompanyId());
        // 公司名称
        attendPlanUpdateDTO.setCompanyName(attendPlanVO.getCompanyName());
        // 考勤方案名称
        attendPlanUpdateDTO.setName(attendRuleUpdateDTO.getPlanName());
        // 方案类型 ; 1-固定时间上下班,2-按班次上下班,3-自由上下班
        attendPlanUpdateDTO.setPlanType(attendRuleUpdateDTO.getPlanType());
        // 打卡地点
        if (attendRuleUpdateDTO.getLocationList() != null) {
            attendPlanUpdateDTO.setPunchLocationJson(HtmlUtils.htmlUnescape(JSON.toJSONString(attendRuleUpdateDTO.getLocationList())));
        }
        // 打卡WIFIJSON
        if (attendRuleUpdateDTO.getWifiList() != null) {
            attendPlanUpdateDTO.setPunchWifiJson(JSON.toJSONString(attendRuleUpdateDTO.getWifiList()));
        }
        // 加班规则 ; 1-以加班申请为准,2-以打卡时间为准,3-以加班申请核算打卡记录
        attendPlanUpdateDTO.setOvertimeRule(attendRuleUpdateDTO.getOvertimeRule());
        // 是否同步中国节假日 ; 1-是,0-否
        attendPlanUpdateDTO.setIsSynchLegalHoliday(attendRuleUpdateDTO.getIsSynchLegalHoliday());
        // 是否手机提醒 ; 1-是,0-否
        attendPlanUpdateDTO.setIsRemind(attendRuleUpdateDTO.getIsRemind());
        // 上班前x分钟
        attendPlanUpdateDTO.setRemindBefore(attendRuleUpdateDTO.getRemindBefore());
        // 下班后x分钟
        attendPlanUpdateDTO.setRemindAfter(attendRuleUpdateDTO.getRemindAfter());
        // 员工打卡时必须拍照 ; 1-是,0-否
        attendPlanUpdateDTO.setIsTakePhoto(attendRuleUpdateDTO.getIsTakePhoto());
        // 只能拍照 ; 备注不允许上传本地图片;1-是,0-否
        attendPlanUpdateDTO.setIsOnlyPhoto(attendRuleUpdateDTO.getIsOnlyPhoto());
        // 范围外打卡 ; 1-允许范围外打卡，记录为地点异常,2-允许范围外打卡，记录为正常外勤,3-不允许范围外打卡
        attendPlanUpdateDTO.setPunchRange(attendRuleUpdateDTO.getPunchRange());
        // 是否补卡申请 ; 1-是,0-否
        attendPlanUpdateDTO.setIsMakeUp(attendRuleUpdateDTO.getIsMakeUp());
        // 每月不高于x下次
        attendPlanUpdateDTO.setMaxMakeUpCounts(attendRuleUpdateDTO.getMaxMakeUpCounts());
        // 生效时间 ; 1-明日生效，2-立即生效
        attendPlanUpdateDTO.setTakeEffect(attendRuleUpdateDTO.getTakeEffect());

        // 如果是立即生效  1-明日生效，2-立即生效
        if (attendRuleUpdateDTO.getTakeEffect() == 2) {
            attendPlanService.update(attendPlanUpdateDTO, tokenUser);
        }
        // 如果是明日生效
        else {
            Date startEffectTime = null;
            try {
                startEffectTime = DateUtils.parseDate(org.jsola.hr.common.DateUtils.getBeforeDateByDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd")), "yyyy-MM-dd");
            } catch (ParseException e) {
                e.printStackTrace();
            }

            attendPlanUpdateDTO.setStartEffectTime(startEffectTime);

            AttendPlanDO attendPlanDO = attendPlanUpdateDTO.to(AttendPlanDO.class);
            attendPlanDO.setSiteId(tokenUser.getSiteId());
            attendPlanDO.setGmtCreate(new Date());
            attendPlanDO.setCreateUserId(tokenUser.getUserId());
            attendPlanDO.setModifiedUserId(tokenUser.getUserId());
            attendPlanDO.setGmtModified(new Date());
            attendPlanDO.setValid(true);

            attendPlanDAO.insert(attendPlanDO);
        }

        // 如果是立即生效  1-明日生效，2-立即生效
        if (attendRuleUpdateDTO.getTakeEffect() == 2) {
            try {
                //TODO 更新消息通知，和定时任务
                taskService.cancel(attendRuleUpdateDTO.getId(), "attendNotice");
                sendNotice(attendRuleUpdateDTO.getId());
            } catch (Exception e) {
                log.error("更新考勤打卡消息通知定时任务异常", e);
            }
        }

        LogKit.log("修改考勤规则,考勤规则名称:" + attendRuleUpdateDTO.getPlanName()
                                , ActionType.UPDATE.getValue(), "attendPlan", tokenUser);

        return 1;
    }

    @Override
    public Page<AttendRuleListVO> selectPage(AttendRuleQuery attendRuleQuery, String siteId) {

        Page<AttendRuleListVO> page = new  Page<AttendRuleListVO>();
        List<AttendRuleListVO> entities = new ArrayList<AttendRuleListVO>();

        AttendPlanQuery attendPlanQuery = new AttendPlanQuery();

        attendPlanQuery.setCompanyId(attendRuleQuery.getCompanyId());
        attendPlanQuery.setPageNo(attendRuleQuery.getPageNo());
        attendPlanQuery.setPageSize(attendRuleQuery.getPageSize());
        attendPlanQuery.addOrder("gmtCreate", true);

        Page<AttendPlanListVO> planPage = attendPlanService.selectPage(attendPlanQuery, siteId);

        List<AttendPlanListVO> attendPlanListVOList = planPage.getEntities();
        if (attendPlanListVOList != null && attendPlanListVOList.size() > 0) {

            attendPlanListVOList.forEach(attendPlanListVO -> {

                List<AttendRulePunchTimeAddDTO> punchTimeList = null;

                // 查询额外数据
                AttendExtraJsonQuery attendExtraJsonQuery = new AttendExtraJsonQuery();
                attendExtraJsonQuery.setAttendPalnId(attendPlanListVO.getId());
                attendExtraJsonQuery.setExtraType(ExtraType.ATTEND_PUNCH_TIME.getValue());
                List<AttendExtraJsonDO> attendExtraJsonDOList = attendExtraJsonService.selectDO(attendExtraJsonQuery, siteId);
                if (attendExtraJsonDOList != null && attendExtraJsonDOList.size() > 0) {
                    for (AttendExtraJsonDO attendExtraJsonDO : attendExtraJsonDOList) {
                        punchTimeList = JSON.parseArray(attendExtraJsonDO.getJsonData(), AttendRulePunchTimeAddDTO.class);
                    }
                }

                AttendRuleListVO attendRuleListVO = new AttendRuleListVO();

                attendRuleListVO.setId(attendPlanListVO.getId());
                attendRuleListVO.setPlanName(attendPlanListVO.getName());
                attendRuleListVO.setPunchTimeList(punchTimeList);

                if (StringUtils.isNotEmpty(attendPlanListVO.getPunchLocationJson())) {
                    attendRuleListVO.setLocationList(JSON.parseArray(attendPlanListVO.getPunchLocationJson(), AttendRuleLocationAddDTO.class));
                }
                else {
                    attendRuleListVO.setLocationList(new ArrayList<AttendRuleLocationAddDTO>());
                }
                if (StringUtils.isNotEmpty(attendPlanListVO.getPunchWifiJson())) {
                    attendRuleListVO.setWifiList(JSON.parseArray(attendPlanListVO.getPunchWifiJson(), AttendRuleWifiAddDTO.class));
                }
                else {
                    attendRuleListVO.setWifiList(new ArrayList<AttendRuleWifiAddDTO>());
                }

                entities.add(attendRuleListVO);
            });

        }

        page.setEntities(entities);
        page.setEntityCount(planPage.getEntityCount());
        page.setFirstEntityIndex(planPage.getFirstEntityIndex());
        page.setLastEntityIndex(planPage.getLastEntityIndex());
        page.setPageCount(planPage.getPageCount());
        page.setPageNo(planPage.getPageNo());
        page.setPageSize(planPage.getPageSize());

        return page;
    }

    /**
     * 逻辑删除考勤规则
     *
     * @param id
     * @param tokenUser
     * @return
     */
    @Override
    public int deleteById(String id, TokenUser tokenUser) {

        // 根据考勤规则ID查询
        AttendPlanDO attendPlanDO = attendPlanService.selectDOById(id, tokenUser.getSiteId());
        if (attendPlanDO == null) {
            throw new HrException("考勤规则不能为空");
        }

        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), id);
    }

    /**
     * 校验人员或者部门中的人员是否已经在考勤规则中
     *
     * @param checkStaffInAttendPlanQuery 校验参数
     * @param tokenUser 当前用户
     * @return
     */
    @Override
    public CheckStaffInAttendPlanVO checkStaffInAttendPlan(CheckStaffInAttendPlanQuery checkStaffInAttendPlanQuery, TokenUser tokenUser) {

        CheckStaffInAttendPlanVO checkStaffInAttendPlanVO = new CheckStaffInAttendPlanVO();
        checkStaffInAttendPlanVO.setResult(false);

        // 如果是部门
        if (checkStaffInAttendPlanQuery.getCheckType() == 2) {
        	
        	// 查询考勤打卡参与部门
        	AttendPunchDeptQuery attendPunchDeptQuery = new AttendPunchDeptQuery();
        	attendPunchDeptQuery.setDeptId(checkStaffInAttendPlanQuery.getCheckObjectId());
        	attendPunchDeptQuery.setAttendPlanIdNotEq(checkStaffInAttendPlanQuery.getExcludeAttendPlanId());
        	List<AttendPunchDeptDO> attendPunchDeptList = attendPunchDeptService.selectDO(attendPunchDeptQuery, tokenUser.getSiteId());
        	if (attendPunchDeptList.size() > 0){
        		checkStaffInAttendPlanVO.setResult(true);
        		return checkStaffInAttendPlanVO;
        	}
        }
        // 如果是人员
        if (checkStaffInAttendPlanQuery.getCheckType() == 1) {
        	
        	AttendPunchStaffQuery attendPunchStaffQuery = new AttendPunchStaffQuery();
        	attendPunchStaffQuery.setEmpInfoId(checkStaffInAttendPlanQuery.getCheckObjectId());
        	attendPunchStaffQuery.setAttendPlanIdNotEq(checkStaffInAttendPlanQuery.getExcludeAttendPlanId());
        	List<AttendPunchStaffDO> attendPunchStaffList = attendPunchStaffService.selectDO(attendPunchStaffQuery, tokenUser.getSiteId());
        	if (attendPunchStaffList.size() > 0) {
        		checkStaffInAttendPlanVO.setResult(true);
        		return checkStaffInAttendPlanVO;
        	}

        }
        
        return checkStaffInAttendPlanVO;
    }

    /**
     * 保存打卡部门
     *
     * @param attendPlanId
     * @param deptList
     * @param tokenUser
     */
    private void saveAttendRuleDept(String attendPlanId, List<AttendRuleDeptAddDTO> deptList, CompanyVO companyInfo, TokenUser tokenUser) {

        deptList.forEach(dept ->{

            if (StringUtils.isBlank(dept.getDeptId())) {
                throw new HrException("打卡部门数据不完整");
            }
            if (StringUtils.isBlank(dept.getDeptName())) {
                throw new HrException("打卡部门数据不完整");
            }

            AttendPunchDeptAddDTO attendPunchDeptAddDTO = new AttendPunchDeptAddDTO();
            attendPunchDeptAddDTO.setCompanyId(companyInfo.getId());
            attendPunchDeptAddDTO.setCompanyName(companyInfo.getName());
            attendPunchDeptAddDTO.setDeptId(dept.getDeptId());
            attendPunchDeptAddDTO.setDeptName(dept.getDeptName());
            attendPunchDeptAddDTO.setAttendPlanId(attendPlanId);

            attendPunchDeptService.save(attendPunchDeptAddDTO, tokenUser);
        });

        // 保存额外数据
        AttendExtraJsonAddDTO attendExtraJsonAddDTO = new AttendExtraJsonAddDTO();
        attendExtraJsonAddDTO.setAttendPalnId(attendPlanId);
        attendExtraJsonAddDTO.setExtraType(ExtraType.ATTEND_PUNCH_DEPT.getValue());
        attendExtraJsonAddDTO.setJsonData(JSON.toJSONString(deptList));
        attendExtraJsonService.save(attendExtraJsonAddDTO, tokenUser);
    }

    /**
     * 保存考勤人员
     *
     * @param attendPlanId
     * @param staffList
     * @param tokenUser
     */
    private void saveStaff(String attendPlanId, List<AttendRuleStaffAddDTO> staffList, CompanyVO companyInfo, TokenUser tokenUser) {

        staffList.forEach(staff -> {

//            if (StringUtils.isBlank(staff.getDepInfoId())
//                    || StringUtils.isBlank(staff.getDepInfoName())
//                    || StringUtils.isBlank(staff.getEmpInfoId())
//                    || StringUtils.isBlank(staff.getEmpInfoName())) {
//                throw new HrException("考勤人员数据不完整");
//            }

            if (StringUtils.isBlank(staff.getEmpInfoId())
                    || StringUtils.isBlank(staff.getEmpInfoName())) {
                throw new HrException("考勤人员数据不完整");
            }

            AttendPunchStaffAddDTO attendPunchStaffAddDTO = new AttendPunchStaffAddDTO();

            attendPunchStaffAddDTO.setCompanyId(companyInfo.getId());
            attendPunchStaffAddDTO.setCompanyName(companyInfo.getName());
//            attendPunchStaffAddDTO.setDeptId(staff.getDepInfoId());
//            attendPunchStaffAddDTO.setDeptName(staff.getDepInfoName());
            attendPunchStaffAddDTO.setEmpInfoId(staff.getEmpInfoId());
            attendPunchStaffAddDTO.setEmpInfoName(staff.getEmpInfoName());
            attendPunchStaffAddDTO.setAttendPlanId(attendPlanId);
            attendPunchStaffService.save(attendPunchStaffAddDTO, tokenUser);
        });

        // 保存额外数据
        AttendExtraJsonAddDTO attendExtraJsonAddDTO = new AttendExtraJsonAddDTO();
        attendExtraJsonAddDTO.setAttendPalnId(attendPlanId);
        attendExtraJsonAddDTO.setExtraType(ExtraType.ATTEND_PUNCH_STAFF.getValue());
        attendExtraJsonAddDTO.setJsonData(JSON.toJSONString(staffList));
        attendExtraJsonService.save(attendExtraJsonAddDTO, tokenUser);
    }

    /**
     * 保存白名单
     * @param attendPlanId
     * @param whitelist
     * @param tokenUser
     */
    private void saveWhitelist(String attendPlanId, List<AttendRuleWhitelistAddDTO> whitelist, CompanyVO companyInfo, TokenUser tokenUser) {

        whitelist.forEach(white -> {

//            if (StringUtils.isBlank(white.getDepInfoId())
//                    || StringUtils.isBlank(white.getDepInfoName())
//                    || StringUtils.isBlank(white.getEmpInfoId())
//                    || StringUtils.isBlank(white.getEmpInfoName())) {
//                throw new HrException("白名单数据不完整");
//            }

            if (StringUtils.isBlank(white.getEmpInfoId())
                    || StringUtils.isBlank(white.getEmpInfoName())) {
                throw new HrException("白名单数据不完整");
            }

            AttendWhitelistAddDTO attendWhitelistAddDTO = new AttendWhitelistAddDTO();

            attendWhitelistAddDTO.setCompanyId(companyInfo.getId());
            attendWhitelistAddDTO.setCompanyName(companyInfo.getName());
//            attendWhitelistAddDTO.setDeptId(white.getDepInfoId());
//            attendWhitelistAddDTO.setDeptName(white.getDepInfoName());
            attendWhitelistAddDTO.setEmpInfoId(white.getEmpInfoId());
            attendWhitelistAddDTO.setEmpInfoName(white.getEmpInfoName());
            attendWhitelistAddDTO.setAttendPlanId(attendPlanId);

            attendWhitelistService.save(attendWhitelistAddDTO, tokenUser);
        });

        // 保存额外数据
        AttendExtraJsonAddDTO attendExtraJsonAddDTO = new AttendExtraJsonAddDTO();
        attendExtraJsonAddDTO.setAttendPalnId(attendPlanId);
        attendExtraJsonAddDTO.setExtraType(ExtraType.ATTEND_WHITELIST.getValue());
        attendExtraJsonAddDTO.setJsonData(JSON.toJSONString(whitelist));
        attendExtraJsonService.save(attendExtraJsonAddDTO, tokenUser);
    }

    /**
     * 保存汇报对象
     *
     * @param attendPlanId
     * @param reportList
     * @param tokenUser
     */
    private void saveReport(String attendPlanId, List<AttendRuleReportAddDTO> reportList, CompanyVO companyInfo, TokenUser tokenUser) {

        reportList.forEach( report ->{

            if (StringUtils.isBlank(report.getDepInfoId())
                    || StringUtils.isBlank(report.getDepInfoName())
                    || StringUtils.isBlank(report.getEmpInfoId())
                    || StringUtils.isBlank(report.getEmpInfoName())) {
                throw new HrException("汇报对象数据不完整");
            }

            AttendReportAddDTO attendReportAddDTO = new AttendReportAddDTO();
            attendReportAddDTO.setAttendPlanId(attendPlanId);
            attendReportAddDTO.setCompanyId(companyInfo.getId());
            attendReportAddDTO.setCompanyName(companyInfo.getName());
            attendReportAddDTO.setDeptId(report.getDepInfoId());
            attendReportAddDTO.setDeptName(report.getDepInfoName());
            attendReportAddDTO.setEmpInfoId(report.getEmpInfoId());
            attendReportAddDTO.setEmpInfoName(report.getEmpInfoName());

            attendReportService.save(attendReportAddDTO, tokenUser);
        });

        // 保存额外数据
        AttendExtraJsonAddDTO attendExtraJsonAddDTO = new AttendExtraJsonAddDTO();
        attendExtraJsonAddDTO.setAttendPalnId(attendPlanId);
        attendExtraJsonAddDTO.setExtraType(ExtraType.ATTEND_REPORT.getValue());
        attendExtraJsonAddDTO.setJsonData(JSON.toJSONString(reportList));
        attendExtraJsonService.save(attendExtraJsonAddDTO, tokenUser);
    }

    /**
     * 保存打卡时间
     *
     * @param attendPlanId
     * @param punchTimeList
     * @param tokenUser
     */
    private void savepPunchTime(String attendPlanId, List<AttendRulePunchTimeAddDTO> punchTimeList, TokenUser tokenUser) {

        if (punchTimeList != null && punchTimeList.size() > 0) {

            for (int i = 0; i < punchTimeList.size(); i++) {
                // 过滤重复数据 - 打卡时段
                List<AttendPeriodDTO> periodList = punchTimeList.get(i).getPeriodList();
                if (periodList != null && periodList.size() > 0) {
                    for (int j = 0; j < periodList.size(); j++) {
                        AttendPeriodDTO period = periodList.get(j);
                        for (int k = periodList.size() - 1 ; k > j; k --) {
                            AttendPeriodDTO periodTmp = periodList.get(k);
                            if (period.getStartPeriod().equals(periodTmp.getStartPeriod())
                                    && period.getEndPeriod().equals(periodTmp.getEndPeriod())
                                    && period.getType().equals(periodTmp.getType())) {
                                periodList.remove(j);
                            }
                        }
                    }
                }
            }
        }

        punchTimeList.forEach(punchTime -> {

            AttendPunchTimeAddDTO attendPunchTimeAddDTO = new AttendPunchTimeAddDTO();

            attendPunchTimeAddDTO.setAttendPlanId(attendPlanId);
            attendPunchTimeAddDTO.setWorkWeekJson(JSON.toJSONString(punchTime.getWorkWeekList()));
            attendPunchTimeAddDTO.setPeriodJson(JSON.toJSONString(punchTime.getPeriodList()));
            attendPunchTimeAddDTO.setIsAdvanc(punchTime.getIsAdvanc());
            attendPunchTimeAddDTO.setIsAllowedLateEarly(punchTime.getIsAllowedLateEarly());
            attendPunchTimeAddDTO.setLateMinute(punchTime.getLateMinute());
            attendPunchTimeAddDTO.setEarlyMinute(punchTime.getEarlyMinute());
            attendPunchTimeAddDTO.setIsAllowLateLate(punchTime.getIsAllowLateLate());
            attendPunchTimeAddDTO.setOffWorkLateMinute(punchTime.getOffWorkLateMinute());
            attendPunchTimeAddDTO.setOnWorkLateMinute(punchTime.getOnWorkLateMinute());
            if (StringUtils.isBlank(punchTime.getEarliestPunchTime())) {
                attendPunchTimeAddDTO.setEarliestPunchTime("80");
            }
            else {
                String earliestPunchTime = punchTime.getEarliestPunchTime();
                if (earliestPunchTime.contains(".")) {
                    earliestPunchTime = earliestPunchTime.substring(0 , earliestPunchTime.indexOf("."));
                }
                attendPunchTimeAddDTO.setEarliestPunchTime(earliestPunchTime);
            }
            if (StringUtils.isBlank(punchTime.getLatestPunchTime())) {
                attendPunchTimeAddDTO.setLatestPunchTime("80");
            }
            else {
                String latestPunchTime = punchTime.getLatestPunchTime();
                if (latestPunchTime.contains(".")) {
                    latestPunchTime = latestPunchTime.substring(0 , latestPunchTime.indexOf("."));
                }
                attendPunchTimeAddDTO.setLatestPunchTime(latestPunchTime);
            }
            if(StringUtils.isBlank(punchTime.getLatestOnPunchTime())){
                attendPunchTimeAddDTO.setLatestOnPunchTime("80");
            }else{
                String latestOnPunchTime = punchTime.getLatestOnPunchTime();
                if (latestOnPunchTime.contains(".")) {
                    latestOnPunchTime = latestOnPunchTime.substring(0 , latestOnPunchTime.indexOf("."));
                }
                attendPunchTimeAddDTO.setLatestOnPunchTime(latestOnPunchTime);
            }
            if(StringUtils.isBlank(punchTime.getEarliestOffPunchTime())){
                attendPunchTimeAddDTO.setEarliestOffPunchTime("80");
            }else{
                String earliestOffPunchTime = punchTime.getEarliestOffPunchTime();
                if (earliestOffPunchTime.contains(".")) {
                    earliestOffPunchTime = earliestOffPunchTime.substring(0 , earliestOffPunchTime.indexOf("."));
                }
                attendPunchTimeAddDTO.setEarliestOffPunchTime(earliestOffPunchTime);
            }
            attendPunchTimeAddDTO.setAfterWorkNeedPunch(punchTime.getAfterWorkNeedPunch());

            attendPunchTimeService.save(attendPunchTimeAddDTO, tokenUser);
        });

        // 保存额外数据
        AttendExtraJsonAddDTO attendExtraJson = new AttendExtraJsonAddDTO();
        attendExtraJson.setAttendPalnId(attendPlanId);
        attendExtraJson.setExtraType(ExtraType.ATTEND_PUNCH_TIME.getValue());
        attendExtraJson.setJsonData(JSON.toJSONString(punchTimeList));
        attendExtraJsonService.save(attendExtraJson, tokenUser);
    }

    /**
     * 保存特殊日期-必须打卡的日期
     *
     * @param attendPlanId
     * @param needPunchDateList
     * @param tokenUser
     */
    private void saveNeedPunchDateList(String attendPlanId, List<NeedPunchDateAddDTO> needPunchDateList, TokenUser tokenUser) {

        needPunchDateList.forEach(needPunchDate -> {
            String reason = needPunchDate.getReason();
            if (StrKit.isNotEmpty(reason)) {
                reason = reason.trim();
                if (reason.length() > 20) {
                    throw new ParamException("事由长度不能超过20， 请检查后重试");
                }
            }

            String yearly = needPunchDate.getDaily().split("-")[0];
            String monthly = needPunchDate.getDaily().split("-")[1];
            String daily = needPunchDate.getDaily().split("-")[2];

            AttendCalendarAddDTO attendCalendarAddDTO = new AttendCalendarAddDTO();

            attendCalendarAddDTO.setAttendPlanId(attendPlanId);
            attendCalendarAddDTO.setYear(yearly);
            attendCalendarAddDTO.setMonth(monthly);
            attendCalendarAddDTO.setDay(daily);
            // 是否考勤 : 1-是,0-否
            attendCalendarAddDTO.setNeedAttend(true);
            attendCalendarAddDTO.setAttendPeriodJson(JSON.toJSONString(needPunchDate.getPeriodList()));
            attendCalendarAddDTO.setReason(reason);
            attendCalendarService.save(attendCalendarAddDTO, tokenUser);
        });

        // 保存额外数据
        AttendExtraJsonAddDTO attendExtraJson = new AttendExtraJsonAddDTO();
        attendExtraJson.setAttendPalnId(attendPlanId);
        attendExtraJson.setExtraType(ExtraType.ATTEND_CALENDAR_NEED_PUNCH.getValue());
        attendExtraJson.setJsonData(JSON.toJSONString(needPunchDateList));
        attendExtraJsonService.save(attendExtraJson, tokenUser);
    }

    /**
     * 保存特殊日期-不必打卡的日期
     *
     * @param attendPlanId
     * @param noNeedPunchDateList
     * @param tokenUser
     */
    private void saveNoNeedPunchDateList(String attendPlanId, List<NoNeedPunchDateAddDTO> noNeedPunchDateList, TokenUser tokenUser) {

        noNeedPunchDateList.forEach(noNeedPunchDate -> {
            String reason = noNeedPunchDate.getReason();
            if (StrKit.isNotEmpty(reason)) {
                reason = reason.trim();
                if (reason.length() > 20) {
                    throw new ParamException("事由长度不能超过20， 请检查后重试");
                }
            }
            String yearly = noNeedPunchDate.getDaily().split("-")[0];
            String monthly = noNeedPunchDate.getDaily().split("-")[1];
            String daily = noNeedPunchDate.getDaily().split("-")[2];

            AttendCalendarAddDTO attendCalendarAddDTO = new AttendCalendarAddDTO();

            attendCalendarAddDTO.setAttendPlanId(attendPlanId);
            attendCalendarAddDTO.setYear(yearly);
            attendCalendarAddDTO.setMonth(monthly);
            attendCalendarAddDTO.setDay(daily);
            // 是否考勤 : 1-是,0-否
            attendCalendarAddDTO.setNeedAttend(false);
            attendCalendarAddDTO.setAttendPeriodJson("");
            attendCalendarAddDTO.setReason(reason);
            attendCalendarService.save(attendCalendarAddDTO, tokenUser);
        });

        // 保存额外数据
        AttendExtraJsonAddDTO attendExtraJson = new AttendExtraJsonAddDTO();
        attendExtraJson.setAttendPalnId(attendPlanId);
        attendExtraJson.setExtraType(ExtraType.ATTEND_PUNCH_DEPT_NO_NEED_PUNCH.getValue());
        attendExtraJson.setJsonData(JSON.toJSONString(noNeedPunchDateList));
        attendExtraJsonService.save(attendExtraJson, tokenUser);
    }

	@Override
	public List<AttendDepInfoTreeVO> selectDepInfoTree(AttendDepInfoTreeQuery attendDepInfoTreeQuery, String siteId) {

        List<AttendDepInfoTreeVO> treeVo = new ArrayList<>();
        List<DepInfoSelectedVO> depInfoSelectedList = new ArrayList<>();

        // 查询公司信息
        CompanyDO companyDO = companyService.selectDOById(attendDepInfoTreeQuery.getCompanyId(), siteId);
        if (companyDO == null) {
            throw new HrException("未查询到公司信息");
        }

        // 添加的时候
        if (StringUtils.isBlank(attendDepInfoTreeQuery.getAttendPlanId())) {
            // 查询公司下的所有部门
            DepInfoQuery depInfoQuery = new DepInfoQuery();
            depInfoQuery.setCompanyId(attendDepInfoTreeQuery.getCompanyId());
            List<DepInfoDO> depInfoList = depInfoService.selectDO(depInfoQuery, siteId);
            if (depInfoList.size() > 0) {
                for (int i = 0; i < depInfoList.size(); i++) {

                    DepInfoDO depInfoDO = depInfoList.get(i);
                    DepInfoSelectedVO depInfoSelectedVO = depInfoDO.to(DepInfoSelectedVO.class);
                    depInfoSelectedVO.setIsSelected(false);
                    depInfoSelectedVO.setIsDept(true);

                    if (depInfoDO.getCode().equals(attendDepInfoTreeQuery.getCompanyId())) {
                        depInfoSelectedVO.setParentId("0");
                    }

                    depInfoSelectedList.add(depInfoSelectedVO);

                    // 追加部门下的人员
                    AttendEmpInfoQuery attendEmpInfoQuery = new AttendEmpInfoQuery();
                    attendEmpInfoQuery.setDepInfoId(depInfoSelectedVO.getId());
                    attendEmpInfoQuery.setType(attendDepInfoTreeQuery.getType());
                    attendEmpInfoQuery.setAttendPlanId(attendDepInfoTreeQuery.getAttendPlanId());
                    attendEmpInfoQuery.setCompanyId(attendDepInfoTreeQuery.getCompanyId());
                    List<AttendEmpInfoVO> attendEmpInfoList = this.selectEmpInfo(attendEmpInfoQuery, siteId);

                    if (attendEmpInfoList != null && attendEmpInfoList.size() > 0) {
                        for (AttendEmpInfoVO empInfo: attendEmpInfoList) {
                            DepInfoSelectedVO depInfoSel = new DepInfoSelectedVO();
                            depInfoSel.setId(empInfo.getId());
                            depInfoSel.setName(empInfo.getName());
                            depInfoSel.setParentId(empInfo.getDeptId());
                            depInfoSel.setIsSelected(empInfo.getIsSelected());
                            depInfoSel.setIsDept(false);
                            depInfoSel.setParentName(empInfo.getDeptName());
                            depInfoSel.setPositionName(empInfo.getPositionName());
                            depInfoSel.setDepName(empInfo.getDeptName());
                            depInfoSel.setIdCard(empInfo.getIdCard());
                            depInfoSel.setEmpId(empInfo.getId());
                            depInfoSel.setUserId(empInfo.getUserId());
                            depInfoSel.setGmtCreate(empInfo.getGmtCreate());
                            depInfoSelectedList.add(depInfoSel);
                        }
                    }
                }
            }

            // 转成树形
            List<DepInfoSelectedVO> depInfoTreeList = TreeKit.toTreeList(depInfoSelectedList);
            // 转成VO树形
            treeVo = TreeKit.toTreeVoList(depInfoTreeList, AttendDepInfoTreeVO.class);

            log.info("添加-查询部门和人员的树形结构:" + JSON.toJSONString(treeVo));

			return treeVo;
		}
		
		// 修改或查看打卡人员
		if (StringUtils.isNotBlank(attendDepInfoTreeQuery.getAttendPlanId()) 
										&& attendDepInfoTreeQuery.getType() == 1) {
            DepInfoQuery depInfoQuery = new DepInfoQuery();
            depInfoQuery.setCompanyId(attendDepInfoTreeQuery.getCompanyId());
            List<DepInfoDO> depInfoList = depInfoService.selectDO(depInfoQuery, siteId);
            if (depInfoList.size() > 0) {
                for (int i = 0; i < depInfoList.size(); i++) {
                    DepInfoDO depInfoDO = depInfoList.get(i);
                    DepInfoSelectedVO depInfoSelectedVO = depInfoDO.to(DepInfoSelectedVO.class);

                    // 是否选中
                    // 查询考勤打卡参与部门
                    AttendPunchDeptQuery attendPunchDeptQuery = new AttendPunchDeptQuery();
                    attendPunchDeptQuery.setAttendPlanId(attendDepInfoTreeQuery.getAttendPlanId());
                    attendPunchDeptQuery.setDeptId(depInfoDO.getId());
                    List<AttendPunchDeptDO> deptList = attendPunchDeptService.selectDO(attendPunchDeptQuery, siteId);
                    if (deptList.size() > 0) {
                        depInfoSelectedVO.setIsSelected(true);
                    }
                    else {
                        depInfoSelectedVO.setIsSelected(false);
                    }
                    depInfoSelectedVO.setIsDept(true);
                    depInfoSelectedList.add(depInfoSelectedVO);

                    // 追加部门下的人员
                    AttendEmpInfoQuery attendEmpInfoQuery = new AttendEmpInfoQuery();
                    attendEmpInfoQuery.setDepInfoId(depInfoSelectedVO.getId());
                    attendEmpInfoQuery.setType(attendDepInfoTreeQuery.getType());
                    attendEmpInfoQuery.setAttendPlanId(attendDepInfoTreeQuery.getAttendPlanId());
                    attendEmpInfoQuery.setCompanyId(attendDepInfoTreeQuery.getCompanyId());
                    List<AttendEmpInfoVO> attendEmpInfoList = this.selectEmpInfo(attendEmpInfoQuery, siteId);

                    if (attendEmpInfoList != null && attendEmpInfoList.size() > 0) {
                        for (AttendEmpInfoVO empInfo: attendEmpInfoList) {
                            DepInfoSelectedVO depInfoSel = new DepInfoSelectedVO();
                            depInfoSel.setId(empInfo.getId());
                            depInfoSel.setName(empInfo.getName());
                            depInfoSel.setParentId(depInfoSelectedVO.getId());
                            depInfoSel.setIsSelected(empInfo.getIsSelected());
                            depInfoSel.setIsDept(false);
                            depInfoSel.setParentName(depInfoSelectedVO.getName());
                            depInfoSel.setPositionName(empInfo.getPositionName());
                            depInfoSel.setDepName(empInfo.getDeptName());
                            depInfoSel.setIdCard(empInfo.getIdCard());
                            depInfoSel.setEmpId(empInfo.getId());
                            depInfoSel.setUserId(empInfo.getUserId());
                            depInfoSelectedList.add(depInfoSel);
                        }
                    }
                }
            }

            // 转成树形
            List<DepInfoSelectedVO> depInfoTreeList = TreeKit.toTreeList(depInfoSelectedList);
            // 转成VO树形
            treeVo = TreeKit.toTreeVoList(depInfoTreeList,AttendDepInfoTreeVO.class);

            log.info("修改-查询部门和人员的树形结构:" + JSON.toJSONString(treeVo));
		}
		
		return treeVo;
	}
	
	/**
	 * 查询部门下的人员
	 */
	@Override
	public List<AttendEmpInfoVO> selectEmpInfo(AttendEmpInfoQuery attendEmpInfoQuery, String siteId) {
		
		EmpDepQuery empDepQuery = new EmpDepQuery();
		empDepQuery.setDepId(attendEmpInfoQuery.getDepInfoId());
        empDepQuery.setCompanyId(attendEmpInfoQuery.getCompanyId());
		List<EmpDepDO> empDepList = empDepService.selectDO(empDepQuery, siteId);
		if (empDepList.size() <= 0) {
			return new ArrayList<>();
		}
		
		List<AttendEmpInfoVO> attendEmpInfoList = new ArrayList<AttendEmpInfoVO>();
		for (EmpDepDO empDepDO : empDepList) {

		    // 查询员工信息
            EmpInfoListVO empInfo = empInfoService.selectEmpAndDepPositionById(empDepDO.getEmpId(), siteId);
            if (Objects.isNull(empInfo)) {
                continue;
            }

            // 查询部门信息
            DepInfoVO depInfoVO = depInfoService.selectById(empDepDO.getDepId(), siteId);

			AttendEmpInfoVO attendEmpInfoVO = new AttendEmpInfoVO();
            attendEmpInfoVO.setId(empInfo.getId());
			attendEmpInfoVO.setIsSelected(false);
            attendEmpInfoVO.setName(empInfo.getName());
            attendEmpInfoVO.setIsDept(false);
            attendEmpInfoVO.setDeptId(depInfoVO.getId());
            attendEmpInfoVO.setDeptName(depInfoVO.getName());
            attendEmpInfoVO.setPositionName(empInfo.getPositionName());
            attendEmpInfoVO.setIdCard(empInfo.getIdCard());
            attendEmpInfoVO.setUserId(empInfo.getUserId());
            attendEmpInfoVO.setGmtCreate(empInfo.getGmtCreate());
			attendEmpInfoList.add(attendEmpInfoVO);
		}
		
		// 如果是添加
		if (StringUtils.isBlank(attendEmpInfoQuery.getAttendPlanId())) {
			return attendEmpInfoList;
		}
		
		// 如果是查询或修改 打卡人员
		if (StringUtils.isNotBlank(attendEmpInfoQuery.getAttendPlanId())
										&& attendEmpInfoQuery.getType() == 1) {
			
			// 查询考勤打卡参与人员
			AttendPunchStaffQuery attendPunchStaffQuery = new AttendPunchStaffQuery();
			attendPunchStaffQuery.setAttendPlanId(attendEmpInfoQuery.getAttendPlanId());
			List<AttendPunchStaffDO> attendPunchStaffList = attendPunchStaffService.selectDO(attendPunchStaffQuery, siteId);
			if (attendPunchStaffList.size() <= 0) {
				return attendEmpInfoList;
			}

			for (int i = 0; i < attendEmpInfoList.size(); i++) {
				
				AttendEmpInfoVO attendEmpInfoVO = attendEmpInfoList.get(i);
                attendEmpInfoVO.setIsDept(false);
				for (AttendPunchStaffDO attendPunchStaffDO : attendPunchStaffList) {
					if (attendEmpInfoVO.getId().equals(attendPunchStaffDO.getEmpInfoId())) {
						attendEmpInfoVO.setIsSelected(true);
						break;
					}
				}
			} //  end for
		}
		
		// 如果是查询或修改白名单
		if (StringUtils.isNotBlank(attendEmpInfoQuery.getAttendPlanId())
										&& attendEmpInfoQuery.getType() == 2) {
			
			// 查询白名单
	        AttendWhitelistQuery attendWhitelistQuery = new AttendWhitelistQuery();
	        attendWhitelistQuery.setAttendPlanId(attendEmpInfoQuery.getAttendPlanId());
	        List<AttendWhitelistDO> attendWhitelistDOList = attendWhitelistService.selectDO(attendWhitelistQuery, siteId);
	        if (attendWhitelistDOList.size() <= 0) {
	        	return attendEmpInfoList;
	        }
	        
			for (int i = 0; i < attendEmpInfoList.size(); i++) {
				
				AttendEmpInfoVO attendEmpInfoVO = attendEmpInfoList.get(i);
                attendEmpInfoVO.setIsDept(false);
				for (AttendWhitelistDO attendWhitelistDO : attendWhitelistDOList) {
					if (attendEmpInfoVO.getId().equals(attendWhitelistDO.getEmpInfoId())) {
						attendEmpInfoVO.setIsSelected(true);
						break;
					}
				}
			} //  end for
		}
		
		// 如果是查询或修改汇报对象
		if (StringUtils.isNotBlank(attendEmpInfoQuery.getAttendPlanId())
										&& attendEmpInfoQuery.getType() == 3) {
			
	        AttendReportQuery attendReportQuery = new AttendReportQuery();
	        attendReportQuery.setAttendPlanId(attendEmpInfoQuery.getAttendPlanId());
	        List<AttendReportDO> attendReportDOList = attendReportService.selectDO(attendReportQuery, siteId);
	        if (attendReportDOList.size() <= 0) {
	        	return attendEmpInfoList;
	        }
			
			for (int i = 0; i < attendEmpInfoList.size(); i++) {
				
				AttendEmpInfoVO attendEmpInfoVO = attendEmpInfoList.get(i);
                attendEmpInfoVO.setIsDept(false);
				for (AttendReportDO attendReportDO : attendReportDOList) {
					if (attendEmpInfoVO.getId().equals(attendReportDO.getEmpInfoId())) {
						attendEmpInfoVO.setIsSelected(true);
						break;
					}
				}
			} //  end for
		}
		
		return attendEmpInfoList;
	}

    @Override
    public int deleteAttendPlan(String attendPlanId, TokenUser tokenUser) {

	    // 根据主键ID查询
        AttendPlanDO attendPlanDO = attendPlanService.selectDOById(attendPlanId, tokenUser.getSiteId());
        if (attendPlanDO == null) {
            throw new HrException("考勤规则不存在");
        }

        // 方案类型 ; 1-固定时间上下班,2-按班次上下班,3-自由上下班

        // 考勤打卡时间
        attendPunchTimeService.logicDelByAttendPlanId(attendPlanId, tokenUser);
        // 考勤日历
        attendCalendarService.logicDelByAttendPlanId(attendPlanId, tokenUser);
        // 考勤方案额外数据
        attendExtraJsonService.logicDelByAttendPlanId(attendPlanId, tokenUser);
        // 考勤白名单
        attendWhitelistService.logicDelByAttendPlanId(attendPlanId, tokenUser);
        // 考勤汇报对象
        attendReportService.logicDelByAttendPlanId(attendPlanId, tokenUser);
        // 考勤打卡参与人员
        attendPunchStaffService.logicDelByAttendPlanId(attendPlanId, tokenUser);
        // 考勤打卡参与部门
        attendPunchDeptService.logicDelByAttendPlanId(attendPlanId, tokenUser);

        // 如果是按班次考勤
        if (attendPlanDO.getPlanType() == 2) {

            // 考勤班次
            attendShiftService.logicDelByAttendPlanId(attendPlanId, tokenUser);
            // 排班周期
            attendShiftScheduleCycleService.logicDelByAttendPlanId(attendPlanId, tokenUser);
            // 考勤人员排班表
            attendShiftStaffScheduleService.logicDelByAttendPlanId(attendPlanId, tokenUser);
        }

        AttendPlanDO attendPlan = new AttendPlanDO();
        attendPlan.setId(attendPlanDO.getId());
        attendPlan.setValid(false);

        attendPlanService.updateByIdSelective(attendPlan, tokenUser.getSiteId(), tokenUser.getUserId());

        LogKit.logForDelete("删除考勤规则,主键ID：" + attendPlanId, "attendPlan", attendPlanId, tokenUser);

        return 1;
    }

    @Override
    public int setEffectiveTomorrow(AttendRuleUpdateDTO attendRuleUpdateDTO, TokenUser tokenUser) {

        // 根据考勤方案ID查询
        AttendPlanVO attendPlanVO = attendPlanService.selectById(attendRuleUpdateDTO.getId(), tokenUser.getSiteId());
        if (attendPlanVO == null) {
            throw new HrException("考勤规则不存在");
        }

        // 将旧版本的规则设置为不显示
        AttendPlanDO attendPlan = new AttendPlanDO();
        attendPlan.setId(attendPlanVO.getId());

        attendPlanService.updateByIdSelective(attendPlan, tokenUser.getSiteId(), tokenUser.getUserId());







        return 0;
    }

    @Override
    public void attendPunchRemind() {
        String dayOfWeek = DateKit8.getDayOfWeek(LocalDate.now());
        String today = DateKit8.format(new Date(), "yyyy-MM-dd");

        List<String> attendPlanIds = new ArrayList<>();

        //考勤规则中设置的打卡时间
        AttendExtraJsonQuery attendExtraJsonQuery1 = new AttendExtraJsonQuery();
        attendExtraJsonQuery1.setExtraType(ExtraType.ATTEND_PUNCH_TIME.getValue());
        attendExtraJsonQuery1.setJsonData(dayOfWeek);
        List<AttendExtraJsonDO> attendExtraJsonList = attendExtraJsonService.selectDO(attendExtraJsonQuery1,null);
        List<String> list1 = attendExtraJsonList.stream().map(AttendExtraJsonDO::getAttendPalnId).collect(Collectors.toList());
        attendPlanIds.addAll(list1);

//        //特殊日期打卡
        AttendExtraJsonQuery attendExtraJsonQuery2 = new AttendExtraJsonQuery();
        attendExtraJsonQuery2.setExtraType(ExtraType.ATTEND_CALENDAR_NEED_PUNCH.getValue());
        attendExtraJsonQuery2.setJsonData(today);
        List<AttendExtraJsonDO> attendExtraJsonList2 = attendExtraJsonService.selectDO(attendExtraJsonQuery2,null);
        List<String> list2 = attendExtraJsonList2.stream().map(AttendExtraJsonDO::getAttendPalnId).collect(Collectors.toList());
        attendPlanIds.addAll(list2);

        //特殊日期不打卡
        AttendExtraJsonQuery attendExtraJsonQuery3 = new AttendExtraJsonQuery();
        attendExtraJsonQuery3.setExtraType(ExtraType.ATTEND_PUNCH_DEPT_NO_NEED_PUNCH.getValue());
        attendExtraJsonQuery3.setJsonData(today);
        List<AttendExtraJsonDO> attendExtraJsonList3 = attendExtraJsonService.selectDO(attendExtraJsonQuery3,null);
        List<String> list3 = attendExtraJsonList3.stream().map(AttendExtraJsonDO::getAttendPalnId).collect(Collectors.toList());
        attendPlanIds.removeAll(list3);

        // 查询规则设置了同步节假日
        List<AttendPlanDO> attendPlanDOList = attendPlanService.selectDOByisSynchLegalHoliday(true);
        if (!CollectionUtils.isEmpty(attendPlanDOList)) {
            // 判断该天 该规则的租户的日历是否是公历节假日同步的数据
            for (AttendPlanDO attendPlanDO : attendPlanDOList) {
                org.jsola.admin.query.GregorianCalendarQuery gregorianCalendarQuery = new org.jsola.admin.query.GregorianCalendarQuery();
                gregorianCalendarQuery.setDate(DateKit8.parseDate(today));
                List<org.jsola.admin.entity.GregorianCalendarDO> gregorianCalendarDOList = gregorianCalendarProviderService
                        .selectClendar(gregorianCalendarQuery, attendPlanDO.getSiteId());
                if (CollectionUtils.isEmpty(gregorianCalendarDOList)) {
                    log.error("同步节假日但是日历记录没有生成!");
                } else {
                    if (gregorianCalendarDOList.get(0).getIsCalendar() != null 
                            && gregorianCalendarDOList.get(0).getIsCalendar()) {
                        // 是公历表同步的 先去掉该规则
                        attendPlanIds.removeIf(planId -> planId.equals(attendPlanDO.getId()));
                        if (DateType.WORK_DAY.getValue().equals(gregorianCalendarDOList.get(0).getDateType())) {
                            // 工作日  往list里面添加当前规则
                            attendPlanIds.add(attendPlanDO.getId());
                        }
                    }
                }
            }
        }

        for (String attendPlanId : attendPlanIds) {
            try {
                sendNotice(attendPlanId);
            } catch (Exception e) {
                log.error("根据考勤规则，生成打卡消息通知异常：", e);
            }
        }
    }

    @Override
    public void sendNotice(String attendPlanId) {
        AttendPlanDO attendPlanDO = attendPlanService.selectDOById(attendPlanId);
        ParamException.notNull(attendPlanDO, "考勤方案ID不存在，id:" + attendPlanId);
        if (attendPlanDO.getIsRemind() == null || attendPlanDO.getIsRemind() == false) {
            //不需要发送公众号消息通知，提醒打卡
            return;
        }
        // 同步节假日
        boolean isSend = false;
        if (attendPlanDO.getIsSynchLegalHoliday() != null
                && attendPlanDO.getIsSynchLegalHoliday()) {
            org.jsola.admin.query.GregorianCalendarQuery gregorianCalendarQuery = new org.jsola.admin.query.GregorianCalendarQuery();
            gregorianCalendarQuery.setDate(DateKit8.parseDate(DateKit8.format(new Date(), "yyyy-MM-dd")));
            List<org.jsola.admin.entity.GregorianCalendarDO> gregorianCalendarDOList = gregorianCalendarProviderService
                    .selectClendar(gregorianCalendarQuery, attendPlanDO.getSiteId());
            if (CollectionUtils.isEmpty(gregorianCalendarDOList)) {
                log.error("同步节假日但是日历记录没有生成!");
                return;
            } else {
                if (gregorianCalendarDOList.get(0).getIsCalendar() != null
                        && gregorianCalendarDOList.get(0).getIsCalendar()) {
                    if (!DateType.WORK_DAY.getValue().equals(gregorianCalendarDOList.get(0).getDateType())) {
                        // 休息日  不发送通知
                        return;
                    } else {
                        // 工作日 发送通知
                        isSend = true;
                    }
                }
            }
        }

        //打卡时间
        List<AttendRulePunchTimeAddDTO> punchTimeList = new ArrayList<>();
        // 特殊日期-必须打卡的日期
        List<NeedPunchDateAddDTO> needPunchDateList = new ArrayList<>();
        // 特殊日期-不必打卡的日期
        List<NoNeedPunchDateAddDTO> noNeedPunchDateList = new ArrayList<>();

        AttendExtraJsonQuery attendExtraJsonQuery = new AttendExtraJsonQuery();
        attendExtraJsonQuery.setAttendPalnId(attendPlanId);
        List<AttendExtraJsonDO> attendExtraJsonList = attendExtraJsonService.selectDO(attendExtraJsonQuery, null);
        for (AttendExtraJsonDO attendExtraJson : attendExtraJsonList) {
            //打卡时间
            if (attendExtraJson.getExtraType().equals(ExtraType.ATTEND_PUNCH_TIME.getValue())) {
                punchTimeList = JSON.parseArray(attendExtraJson.getJsonData(), AttendRulePunchTimeAddDTO.class);
            }
            //特殊日期-必须打卡的日期
            if (attendExtraJson.getExtraType().equals(ExtraType.ATTEND_CALENDAR_NEED_PUNCH.getValue())) {
                needPunchDateList = JSON.parseArray(attendExtraJson.getJsonData(), NeedPunchDateAddDTO.class);
            }
            // 考勤日历(不需要打卡)
            if (attendExtraJson.getExtraType().equals(ExtraType.ATTEND_PUNCH_DEPT_NO_NEED_PUNCH.getValue())) {
                // 特殊日期-不必打卡的日期
                noNeedPunchDateList = JSON.parseArray(attendExtraJson.getJsonData(), NoNeedPunchDateAddDTO.class);
            }
        }

        //如果不需要打卡直接返回
        for (NoNeedPunchDateAddDTO noNeedPunchDateAddDTO : noNeedPunchDateList) {
            String daily = noNeedPunchDateAddDTO.getDaily();
            String nowDaily = DateKit8.format2Date(new Date());
            if (nowDaily.equals(daily)) {
                return;
            }
        }

        //上班前X分钟通知，下班后Y分钟通知
        int remindBefore = attendPlanDO.getRemindBefore() == null ? 0 : attendPlanDO.getRemindBefore();
        int remindAfter = attendPlanDO.getRemindAfter() == null ? 0 : attendPlanDO.getRemindAfter();

        //打卡消息提醒
        List<AttendPeriodNoticeDTO> noticeList = new ArrayList<AttendPeriodNoticeDTO>();


        //特殊规定的打卡时间
        for (NeedPunchDateAddDTO needPunchDateAddDTO : needPunchDateList) {
            String daily = needPunchDateAddDTO.getDaily();
            String nowDaily = DateKit8.format2Date(new Date());
            if (nowDaily.equals(daily)) {
                List<AttendPeriodDTO> attendPeriodDTOList = needPunchDateAddDTO.getPeriodList();
                for (AttendPeriodDTO attendPeriodDTO : attendPeriodDTOList) {
                    String startPeriod = attendPeriodDTO.getStartPeriod();
                    String endPeriod = attendPeriodDTO.getEndPeriod();

                    String remindShangbanStr = DateKit8.getCurrentDay("yyyy-MM-dd ") + startPeriod + ":00";
                    Date remindShangban = DateKit8.addMinutes(DateKit8.parse(remindShangbanStr, "yyyy-MM-dd HH:mm:ss"), -remindBefore);
                    String remindXiabanStr = DateKit8.getCurrentDay("yyyy-MM-dd ") + endPeriod + ":00";
                    Date remindXiaban = DateKit8.addMinutes(DateKit8.parse(remindXiabanStr, "yyyy-MM-dd HH:mm:ss"), remindAfter);

                    AttendPeriodNoticeDTO attendPeriodNoticeDTO = new AttendPeriodNoticeDTO();
                    attendPeriodNoticeDTO.setStartPeriod(startPeriod);
                    attendPeriodNoticeDTO.setEndPeriod(endPeriod);
                    attendPeriodNoticeDTO.setRemindStartPeriod(remindShangban);
                    attendPeriodNoticeDTO.setRemindEndPeriod(remindXiaban);
                    noticeList.add(attendPeriodNoticeDTO);
                }
            }
        }


        //通用打卡时间（如果设置了特殊规定的打卡时间，就忽略通用打卡时间）
        if (needPunchDateList.isEmpty() ){
            for (AttendRulePunchTimeAddDTO attendRulePunchTimeAddDTO : punchTimeList) {
                List<String> workWeekList = attendRulePunchTimeAddDTO.getWorkWeekList();
                String dayOfWeek = DateKit8.getDayOfWeek(LocalDate.now());
                if (workWeekList.contains(dayOfWeek) || isSend) {
                    List<AttendPeriodDTO> attendPeriodDTOList = attendRulePunchTimeAddDTO.getPeriodList();
                    for (AttendPeriodDTO attendPeriodDTO : attendPeriodDTOList) {
                        String startPeriod = attendPeriodDTO.getStartPeriod();
                        String endPeriod = attendPeriodDTO.getEndPeriod();

                        String remindShangbanStr = DateKit8.getCurrentDay("yyyy-MM-dd ") + startPeriod + ":00";
                        Date remindShangban = DateKit8.addMinutes(DateKit8.parse(remindShangbanStr, "yyyy-MM-dd HH:mm:ss"), -remindBefore);
                        String remindXiabanStr = DateKit8.getCurrentDay("yyyy-MM-dd ") + endPeriod + ":00";
                        Date remindXiaban = DateKit8.addMinutes(DateKit8.parse(remindXiabanStr, "yyyy-MM-dd HH:mm:ss"), remindAfter);

                        AttendPeriodNoticeDTO attendPeriodNoticeDTO = new AttendPeriodNoticeDTO();
                        attendPeriodNoticeDTO.setStartPeriod(startPeriod);
                        attendPeriodNoticeDTO.setEndPeriod(endPeriod);
                        attendPeriodNoticeDTO.setRemindStartPeriod(remindShangban);
                        attendPeriodNoticeDTO.setRemindEndPeriod(remindXiaban);
                        noticeList.add(attendPeriodNoticeDTO);
                    }
                }
            }
        }

        CompanyDO company = companyService.selectDOByIdNotSite(attendPlanDO.getCompanyId());
        ParamException.notNull(company, "考勤方案对应公司不存在,id:" + attendPlanDO.getCompanyId());

        String siteId = company.getSiteId();

        TokenUser tokenUser = new TokenUser("system", siteId);

        for (AttendPeriodNoticeDTO attendPeriodNoticeDTO : noticeList) {
            try {
                TaskAddDTO taskStartAddDTO = new TaskAddDTO();
                taskStartAddDTO.setSubjectType("attendNotice");
                taskStartAddDTO.setSubjectId(attendPlanId);
                taskStartAddDTO.setDescription("上班考勤打卡通知定时任务");
                taskStartAddDTO.setRunBeanName("hrAttendRuleServiceImpl");
                taskStartAddDTO.setRunType("shangban");
                Date runDate = attendPeriodNoticeDTO.getRemindStartPeriod();
                taskStartAddDTO.setRunDate(runDate);
                taskStartAddDTO.setRunParam(JSONObject.toJSONString(attendPeriodNoticeDTO));
                taskStartAddDTO.setStatus(false);
                // 运行时间晚于当前时间,才发送通知了
                if (runDate != null && runDate.after(new Date())) {
                    TaskVO taskVO = taskService.save(taskStartAddDTO, tokenUser);
                    // 添加运行定时任务
                    Runnable runnable = new attendPunchRemindTask(taskVO.getId());
                    threadPoolTask.schedule(runnable, runDate);
                }
                TaskAddDTO taskEndAddDTO = new TaskAddDTO();
                taskEndAddDTO.setSubjectType("attendNotice");
                taskEndAddDTO.setSubjectId(attendPlanId);
                taskEndAddDTO.setDescription("下班考勤打卡通知定时任务");
                taskEndAddDTO.setRunBeanName("hrAttendRuleServiceImpl");
                taskEndAddDTO.setRunType("xiaban");
                Date runDate2 = attendPeriodNoticeDTO.getRemindEndPeriod();
                taskEndAddDTO.setRunDate(runDate2);
                taskEndAddDTO.setRunParam(JSONObject.toJSONString(attendPeriodNoticeDTO));
                taskEndAddDTO.setStatus(false);
                if (runDate2 != null && runDate2.after(new Date())) {
                    TaskVO taskVO2 = taskService.save(taskEndAddDTO, tokenUser);
                    // 添加运行定时任务
                    Runnable runnable2 = new attendPunchRemindTask(taskVO2.getId());
                    threadPoolTask.schedule(runnable2, runDate2);
                }
            } catch (Exception e) {
                log.error("生成考勤通知定时任务异常,规则id:{}", attendPlanId);
                log.error("生成考勤通知定时任务异常:", e);
            }
        }
    }

    public class attendPunchRemindTask implements Runnable {
        private String taskId;

        attendPunchRemindTask(String taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {
            TaskDO task = taskService.selectDOById(taskId, "");
            if (Objects.nonNull(task)) {
                attendSysNoticeTaskService.runTask(taskId);
            }
        }
    }


    /**
     * 保存加班规则
     *
     * @param attendPlanId
     * @param overtimeRule
     * @param workDayRuleDTO
     * @param nonWorkDayRuleDTO
     * @param tokenUser
     * @return
     */
	private void saveOvertimeRule(String attendPlanId, Integer overtimeRule, WorkDayRuleDTO workDayRuleDTO, NonWorkDayRuleDTO nonWorkDayRuleDTO, TokenUser tokenUser) {

        // 保存加班规则
        AttendOvertimeRuleAddDTO attendOvertimeRuleAddDTO = new AttendOvertimeRuleAddDTO();

        attendOvertimeRuleAddDTO.setAttendPlanId(attendPlanId);
        attendOvertimeRuleAddDTO.setOvertimeType(overtimeRule);
        if (workDayRuleDTO != null) {
            attendOvertimeRuleAddDTO.setWorkDayRuleJson(JSON.toJSONString(workDayRuleDTO));
        }
        else {
            attendOvertimeRuleAddDTO.setWorkDayRuleJson("");
        }
        if (nonWorkDayRuleDTO != null) {
            attendOvertimeRuleAddDTO.setNonWorkDayRuleJson(JSON.toJSONString(nonWorkDayRuleDTO));
        }
        else {
            attendOvertimeRuleAddDTO.setNonWorkDayRuleJson("");
        }
        AttendOvertimeRuleVO attendOvertimeRuleVO = attendOvertimeRuleService.save(attendOvertimeRuleAddDTO, tokenUser);

        // 保存额外数据
        AttendExtraJsonAddDTO attendExtraJson = new AttendExtraJsonAddDTO();
        attendExtraJson.setAttendPalnId(attendPlanId);
        attendExtraJson.setExtraType(ExtraType.OVERTIME_RULE.getValue());
        attendExtraJson.setJsonData(JSON.toJSONString(attendOvertimeRuleVO));
        attendExtraJsonService.save(attendExtraJson, tokenUser);
    }



}
